#include "utilities.h"

//This is the code for the write function which will write it to the corresponding socket.
void searchProcessing(msg_header msgRespHdr, listBuffStruct eventReq)
{
	map<int,writeListData>::iterator it;
	list<searchSTR>::iterator itR;
	list<fromUOIDStr>::iterator itF;
	//This the place where search request is processed
	if((uint8_t)msgRespHdr.msgtype == 0xEC)
	{
		if(printFlag==1)
		{
			cout<<"Got Search Request msg from - "<<eventReq.portNumber<<endl;
		}
		int testUOID = findInUOIDList(msgRespHdr.uoid);
		if(testUOID == 1)
		{
		
		}
		else
		{
			//Put the UOID in uoid queue
			uoidStr tempUOIDStr;
			struct timeval time1;
			gettimeofday(&time1, NULL);
			tempUOIDStr.timeOfTheDay = (double)(time1.tv_sec * (1e+3) + (double)time1.tv_usec/1000);
			tempUOIDStr.msgLifeTime = initMap["MsgLifetime"].asDouble;
			memcpy(tempUOIDStr.msgUOID, msgRespHdr.uoid, 20);
			pthread_mutex_lock(&uoidlock);
			myUOIDList.push_back(tempUOIDStr);
			pthread_mutex_unlock(&uoidlock);
			//if TTL is 1 then do not forward
			if(msgRespHdr.timetolive==1)
			{
				
			}
			else
			{
				//Forward the request to neighbors and reduce TTL by 1
				listBuffStruct lbtemp;
				lbtemp.logtype=2;
				int tempTTL = msgRespHdr.timetolive;
				tempTTL--;
				msgRespHdr.timetolive = tempTTL;
				memset(&lbtemp.listBuff, '\0', 8192);
				fillMsgHdrBuffer(msgRespHdr, lbtemp.listBuff);
				memcpy(&(lbtemp.listBuff[27]),&(eventReq.listBuff[27]),msgRespHdr.datalength);
				lbtemp.datalength = 27 + msgRespHdr.datalength;
				pthread_mutex_lock(&writeMapMutex);
				for(it=writeMap.begin(); it!=writeMap.end(); it++)
				{
					if((*it).first!=eventReq.portNumber)
					{
						//store the uoid in a map so that when it recieves a response then send it to the persone who initiated it.
						fromUOIDStr fromTemp;
						memcpy(fromTemp.uoid, &msgRespHdr.uoid, 20);
						fromTemp.portnumber = eventReq.portNumber;
						myFromUOIDList.push_back(fromTemp);
						lbtemp.portNumber = (*it).first;
						writeListData listDataStr = writeMap[(*it).first];
						listDataStr.response.push_back(lbtemp);
						writeMap[(*it).first] = listDataStr;
					}
				}
				pthread_mutex_unlock(&writeMapMutex);
			}
			search_request searchReq;
			memcpy(&searchReq.searchType, &(eventReq.listBuff[27]), sizeof(uint8_t));
			memset(searchReq.searchQuery, '\0', 512);
			memcpy(&searchReq.searchQuery, &(eventReq.listBuff[28]), msgRespHdr.datalength - sizeof(uint8_t));
			//Clear the searched files list before the new search
			pthread_mutex_lock(&searchFileIdsLock);
			searchFileIdsList.clear();
			pthread_mutex_unlock(&searchFileIdsLock);
			//Search for the files in the index files and store those indexes in searchFileIdsList
			searchFiles(searchReq);
			
			//Make the Response and put in write queue
			//1. Make status responseHdr
			int dataLen = 0;
			search_response searResp;
			memcpy(&searResp.uoid, &msgRespHdr.uoid, 20);
			dataLen = 20;
			int tempLeng = 0;
			memset(&searResp.LenIdAndMeta, '\0', 8192);
			int tempLastStrlen = 0;
			list<search_fileId_results>::iterator itSearFidsList;
			int ksz = 0;
			uint32_t fileLen = 0;
			char filename[128];
			char fileData[512];
			//Take each Index from searchFileIdsList and append it to the listBuff
			pthread_mutex_lock(&searchFileIdsLock);
			for(itSearFidsList = searchFileIdsList.begin(); itSearFidsList != searchFileIdsList.end(); itSearFidsList++)
			{
				memset(fileData, '\0', 512);
				memset(filename, '\0', 128);
				strcpy(filename,initMap["HomeDir"].asStr);
				strcat(filename,"/");
				strcat(filename,"files/");
				char ind[10];
				memset(ind, '\0', 10);
				sprintf(ind, "%d", itSearFidsList->fileIndex);
				strcat(filename, ind);
				strcat(filename, ".meta");
				struct stat st;
				ksz = stat(filename, &st);
				fileLen = (uint32_t)st.st_size;
				memcpy(&(searResp.LenIdAndMeta[tempLeng]), &fileLen, sizeof(uint32_t));
				FILE *fileP;
				fileP = fopen(filename, "r");
				fread(fileData, 1, fileLen, fileP);
				fclose(fileP);
				memcpy(&(searResp.LenIdAndMeta[tempLeng+sizeof(uint32_t)]), itSearFidsList->fileUOID, 20);
				memcpy(&(searResp.LenIdAndMeta[tempLeng+sizeof(uint32_t)+20]), &fileData, fileLen);
				tempLeng = tempLeng + sizeof(uint32_t) + 20 + fileLen;
				tempLastStrlen = sizeof(uint32_t) + 20 + fileLen;
			}
			pthread_mutex_unlock(&searchFileIdsLock);
			
			//If none of index match to the query then do not send any response to the requestor.
			if(!searchFileIdsList.empty())
			{
				uint32_t lastZero = 0;
				dataLen = dataLen + tempLeng;
				memcpy(&(searResp.LenIdAndMeta[tempLeng-tempLastStrlen]), &lastZero, sizeof(uint32_t));
					
				//2. Make header
				char obj[512];
				msg_header responseHdr;
				strcpy(obj,"SHRS");
				createMsgHdrStr(&responseHdr,0xEB,initMap["TTL"].asDouble,obj,node_instance_id, dataLen);
				
				//copy the above structures into message response structure
				listBuffStruct lbtemp;
				lbtemp.logtype=1;
				memset(&lbtemp.listBuff, '\0', 8192);
				fillMsgHdrBuffer(responseHdr, lbtemp.listBuff);
				
				memcpy(&(lbtemp.listBuff[27]), &(searResp.uoid), 20);
				memcpy(&(lbtemp.listBuff[47]), &(searResp.LenIdAndMeta), dataLen-20);
				lbtemp.datalength = 27+dataLen;
				lbtemp.portNumber = eventReq.portNumber;
				pthread_mutex_lock(&writeMapMutex);
				writeListData listDataStr = writeMap[eventReq.portNumber];
				//put them in the writeQueue.
				listDataStr.response.push_back(lbtemp);
				writeMap[eventReq.portNumber] = listDataStr;
				pthread_mutex_unlock(&writeMapMutex);
			}
		}
	}
	if((uint8_t)msgRespHdr.msgtype == 0xEB)
	{
		if(printFlag==1)
		{
			cout<<"Got Search Response msg from - "<<eventReq.portNumber<<endl;
		}
		char UOIDtemp[20];
		memset(UOIDtemp, '\0', 20);
		memcpy(&UOIDtemp, &(eventReq.listBuff[27]), 20);
		//If I am the request initiator then store the response onto a .out file
		bool match = false;
		for(itR=searchResponseList.begin(); itR!=searchResponseList.end(); itR++)
		{
			searchSTR temp = *itR;
			if(memcmp(UOIDtemp,temp.uoid,20)==0)
			{
				if(printFlag==1)
				{
					cout<<"MATCH FOUND"<<endl;
				}
				match = true;
				break;
			}
		}
		if(match==true)
		{
			search_response tempResp;
			memcpy(&(tempResp.uoid), &(eventReq.listBuff[27]), 20);
			memcpy(&(tempResp.LenIdAndMeta), &(eventReq.listBuff[47]), msgRespHdr.datalength-20);
			
			int tempIndex = 0;
			uint32_t tempLeng = -1;
			char fileId[20];
			char meta[512];
			while(1)
			{
				memset(fileId, '\0', 20);
				memset(meta, '\0', 512);
				memcpy(&tempLeng, &(tempResp.LenIdAndMeta[tempIndex]), sizeof(uint32_t));
				if(tempLeng != 0)
				{
					memcpy(&fileId, &(tempResp.LenIdAndMeta[tempIndex + sizeof(uint32_t)]), 20);
					memcpy(&meta, &(tempResp.LenIdAndMeta[tempIndex + sizeof(uint32_t) + 20]), tempLeng);
					
					pthread_mutex_lock(&getIndexLock);
					cout<<"["<<getIndex<<"] ";
					cout<<"FileID =";
					for(int m=0;m<20;m++)
					{
						printf("%02x", (uint8_t)fileId[m]);
					}
					cout<<endl;
					char sha1[20];
					displaySearchResults(meta, sha1);
					
					//Copy the result to a list of structures which is used while GET request
					getSearchResults gSearRes;
					gSearRes.getIndex = getIndex;
					memset(gSearRes.fileId, '\0', 20);
					memcpy(&gSearRes.fileId, &(fileId[0]), 20);
					memcpy(&gSearRes.fileSha1, &(sha1[0]), 20);
					pthread_mutex_lock(&searchResultLock);
					searchResultList.push_back(gSearRes);
					pthread_mutex_unlock(&searchResultLock);
					//increment the index of searche results count
					getIndex++;
					pthread_mutex_unlock(&getIndexLock);
				}
				else
				{
					memcpy(&fileId, &(tempResp.LenIdAndMeta[tempIndex + sizeof(uint32_t)]), 20);
					memcpy(&meta, &(tempResp.LenIdAndMeta[tempIndex + sizeof(uint32_t) + 20]), msgRespHdr.datalength-tempIndex-20-20-sizeof(uint32_t));
					
					pthread_mutex_lock(&getIndexLock);
					cout<<"["<<getIndex<<"] ";
					cout<<"FileID=";
					for(int m=0;m<20;m++)
					{
						printf("%02x", (uint8_t)fileId[m]);
					}
					cout<<endl;
					char sha1[20];
					displaySearchResults(meta, sha1);
					
					//Copy the result to a list of structures which is used while GET request
					getSearchResults gSearRes;
					gSearRes.getIndex = getIndex;
					memset(gSearRes.fileId, '\0', 20);
					memcpy(&gSearRes.fileId, &(fileId[0]), 20);
					memcpy(&gSearRes.fileSha1, &(sha1[0]), 20);
					pthread_mutex_lock(&searchResultLock);
					searchResultList.push_back(gSearRes);
					pthread_mutex_unlock(&searchResultLock);
					//increment the index of searche results count
					getIndex++;
					pthread_mutex_unlock(&getIndexLock);
					break;
				}
				tempIndex = tempIndex + tempLeng + sizeof(uint32_t) + 20;
			}
		}
		else
		{
			//If I am not the request initiator then send it to the person who requested/forwarded the request
			int testUOID = findInUOIDList(msgRespHdr.uoid);
			if(testUOID == 1)
			{
				
			}
			else
			{
				//Store the request in the uoid queue.
				uoidStr tempUOIDStr;
				struct timeval time1;
				gettimeofday(&time1, NULL);
				tempUOIDStr.timeOfTheDay = (double)(time1.tv_sec * (1e+3) + (double)time1.tv_usec/1000);
				tempUOIDStr.msgLifeTime = initMap["MsgLifetime"].asDouble;
				memcpy(tempUOIDStr.msgUOID, msgRespHdr.uoid, 20);
				pthread_mutex_lock(&uoidlock);
				myUOIDList.push_back(tempUOIDStr);
				pthread_mutex_unlock(&uoidlock);
				//If TTL is 1 then do not forward
				if(msgRespHdr.timetolive==1)
				{
			
				}
				else
				{
					//Sending the response to the forwarder
					listBuffStruct lbtemp;
					int tempTTL = msgRespHdr.timetolive;
					tempTTL--;
					msgRespHdr.timetolive = tempTTL;
					memset(&lbtemp.listBuff, '\0', 8192);
					fillMsgHdrBuffer(msgRespHdr, lbtemp.listBuff);
					memcpy(&(lbtemp.listBuff[27]),&(eventReq.listBuff[27]),msgRespHdr.datalength);
					lbtemp.datalength = eventReq.datalength;
					lbtemp.portNumber = eventReq.portNumber;
					lbtemp.logtype=2;
					
					pthread_mutex_lock(&writeMapMutex);
					for(itF=myFromUOIDList.begin(); itF!=myFromUOIDList.end(); itF++)
					{
						fromUOIDStr temp = *itF;
						if(memcmp(UOIDtemp,temp.uoid,20)==0)
						{
							writeListData listDataStr = writeMap[temp.portnumber];
							listDataStr.response.push_back(lbtemp);
							writeMap[temp.portnumber] = listDataStr;
							break;
						}
					}
					pthread_mutex_unlock(&writeMapMutex);
				}
			}
		}
	}
}
